#include "GizmoManipulator.h"

#include <osg/Notify>
#include <osg/io_utils>
#include <osg/Quat>
#include <osg/Notify>
#include <osg/io_utils>
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osgUtil/IntersectVisitor>
#include <osg/MatrixTransform>

#include <sstream>
#include <assert.h>

using namespace osg;
using namespace osgGA;

//--------------------------------------------------------------------
GizmoManipulator::GizmoManipulator()  
//--------------------------------------------------------------------
{
  _axis = NONE;
  _action = NOP;
  osg::Node *n = osgDB::readNodeFile("gizmo.osg");
  _gizmo = dynamic_cast<osg::MatrixTransform*>(n);
  assert( _gizmo.get() );

  // mat->geode->geometry->stateset->material
  for( size_t i=0; i<_gizmo->getNumChildren(); i++)
  {
    osg::MatrixTransform *t = dynamic_cast<osg::MatrixTransform*>( _gizmo->getChild(i) );
    if(t)
    {
      _axis = NONE;
      if ( t->getName() == "GIZMO_X" ) _axis = X;
      if ( t->getName() == "GIZMO_Y" ) _axis = Y;
      if ( t->getName() == "GIZMO_Z" ) _axis = Z;
      
      if(_axis != NONE)
      {
        _gizmo_axis[_axis] = t;
        for( size_t j=0; j<t->getNumChildren(); j++)
        {
          osg::Geode *geode = dynamic_cast<osg::Geode*>( t->getChild(j) );
          if( geode)
          {
            for( size_t k=0; k<geode->getNumDrawables(); k++)
            {
              osg::Drawable *geom = dynamic_cast<osg::Drawable*>( geode->getDrawable(k) );
              if( geom)
              {
                osg::StateSet *ss = geom->getStateSet();
                if(ss)
                {
                  StateAttribute *sa = ss->getAttribute(StateAttribute::MATERIAL);
                  osg::Material *mat = dynamic_cast<osg::Material*>( sa );
                  if( mat )
                  { 
                    _axis_material[_axis] = mat;
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  
  assert(_axis_material[X].get() && _axis_material[Y].get() && _axis_material[Z].get());
  assert(_gizmo_axis[X].get() && _gizmo_axis[Y].get() && _gizmo_axis[Z].get());
  _axis = NONE;
}
//--------------------------------------------------------------------
GizmoManipulator::~GizmoManipulator()
//--------------------------------------------------------------------
{
}
//--------------------------------------------------------------------
bool GizmoManipulator::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa)
//--------------------------------------------------------------------
{
    osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
    if(!viewer) return false;

    switch(ea.getEventType())
    {
        case(osgGA::GUIEventAdapter::PUSH):
        {

            osg::MatrixTransform *t = pickTransform(viewer,ea);

            _axis = NONE;
            if(t) 
            {
              if ( t == _gizmo_axis[0] ) _axis = X;
              if ( t == _gizmo_axis[1] ) _axis = Y;
              if ( t == _gizmo_axis[2] ) _axis = Z;
            } 
            
            if( ea.getModKeyMask() & GUIEventAdapter::MODKEY_CTRL)
            {
              if(_axis == NONE )
                select(t);
              else
                select(0);  
              return false;
            }
            
            if( _axis != NONE)
            {
              _camera_manip = viewer->getCameraManipulator();
              viewer->setCameraManipulator( 0 );
              mouse_down();
              return true;
            }
        }    
        case(osgGA::GUIEventAdapter::DRAG):
        {
          if( _sel.get() && _axis!=NONE ) mouse_move();
          return true;
        }
        case(osgGA::GUIEventAdapter::RELEASE):
        {
          if( _sel.get() && _axis!=NONE )
          {
            viewer->setCameraManipulator( _camera_manip.get() );
            mouse_up();
            _axis=NONE;
          }
          return true;
        }
        default:
            return false;
    }
}
//--------------------------------------------------------------------
osg::MatrixTransform* GizmoManipulator::pickTransform(osgViewer::Viewer* viewer, const osgGA::GUIEventAdapter& ea)
//--------------------------------------------------------------------
{
  osg::notify(osg::INFO) << "----: "  <<std::endl;

  osgUtil::LineSegmentIntersector::Intersections intersections;
  if (viewer->computeIntersections(ea.getX(),ea.getY(),intersections))
  {
    osgUtil::LineSegmentIntersector::Intersections::iterator hitr;  
    for(hitr = intersections.begin(); hitr != intersections.end(); ++hitr)
    {
      if (!hitr->nodePath.empty())
      {
        osg::NodePath np = hitr->nodePath;

				for(size_t i = np.size() - 1; i > 0; i-- ) 
        {
						osg::Node *n = np[i]; 
            osg::MatrixTransform *transform = dynamic_cast<osg::MatrixTransform*>(n);
            if(transform) return transform;
            //osg::notify(osg::INFO) << "class: " << n->className() <<std::endl;
        }
        break;
      }
    }
  }
}
//--------------------------------------------------------------------
void GizmoManipulator::select(MatrixTransform *t)
//--------------------------------------------------------------------
{
  if( t == _sel ) return;
  if( _sel.get() ) _sel->removeChild(_gizmo.get());
  _sel = t;
  if( _sel.get() ) 
  {
    //scala il Gizmo in base all'oggetto selezionato
    //const osg::BoundingSphere& bs= t->getBound();
    //t->dirtyBound();
    int sz = t->getBound()._radius * 1.1;
    //osg::notify(osg::INFO) << "class: " << n->className() <<std::endl;
    _gizmo->setMatrix(  osg::Matrixd::scale(sz, sz, sz) );

    //attacca il Gizmo sotto la transform
    _sel->addChild(_gizmo.get());
  }
}
//--------------------------------------------------------------------
void GizmoManipulator::mouse_down()
//--------------------------------------------------------------------
{
  //l'asse diventa giallo
  _axis_material[_axis]->setDiffuse( osg::Material::FRONT, Vec4(1,1,0,1));
  _axis_material[_axis]->setAmbient( osg::Material::FRONT, Vec4(1,1,0,1));
}
//--------------------------------------------------------------------
void GizmoManipulator::mouse_move()
//--------------------------------------------------------------------
{
  // fai l'azione
}
//--------------------------------------------------------------------
void GizmoManipulator::mouse_up()
//--------------------------------------------------------------------
{
  // fine azione -- l'asse giallo ritorna normale
  Vec4 color;
  if(_axis==X) color = Vec4(1,0,0,1);
  if(_axis==Y) color = Vec4(0,1,0,1);
  if(_axis==Z) color = Vec4(0,0,1,1);
  _axis_material[_axis]->setDiffuse( osg::Material::FRONT, color );
  _axis_material[_axis]->setAmbient( osg::Material::FRONT, color );
}
